
import path from 'path'
import fs from 'fs-extra'
import ini from 'ini'
import inquirer from 'inquirer'
import Datepicker from 'inquirer-datepicker'
import moment from 'moment'
import _ from 'lodash'
import ora from 'ora'
import Table from 'tty-table'
import { Protobuffer, Socket } from 'gm-api-sdk'
import { GMC_CONF, GMC_PATH, getSocketList, getInterfaces, getInterfaceList, getProjectList } from './base'

inquirer.registerPrompt('datepicker', Datepicker)

const paramsTableHeader = [
  {
    value : "key",
    //headerColor : "cyan",
    //color: "white",
    //align : "center",
    //paddingLeft : 5,
    //width : 20
  },
  {
    value : "value",
    //headerColor : "cyan",
    //color: "white",
    //align : "center",
    //paddingLeft : 5,
    //width : 30
  }
]
const CMsgTableHeader = [
  {
    value : "key",
  },
  {
    value : "value",
    width : 30
  }
]
const tableStyle = {
  borderStyle : 2,
  paddingBottom : 0,
  headerAlign : "center",
  align : "center",
  color : "white",
  truncate: "..."
} 

export default () => {
  let options = null
  let projectList = getProjectList()
  if (projectList.length === 0) {
    console.log(`\n   还没有项目建立，先去创建一个 !\n`)
    return
  }
  return inquirer.prompt([
    {
      type: 'list',
      name: 'api',
      message: '请选择API入口：',
      choices: getProjectList()
    }
  ])
  .then( ret => {
    options = { 
      ...options, 
      api: ret.api.replace(/(\[)([a-zA-Z]+)(\])(\s)(\-){2}(\s)(\S+)/, '$2')
    }
    let socketList = getSocketList(options.api)
    if (socketList.length === 0) {
      throw new Error('还没有Socket连接 !')
    }
    return inquirer.prompt([
      {
        type: 'list',
        name: 'socket',
        message: '请选择需要连接的服务器：',
        choices: getSocketList(options.api)
      }
    ])
  })
  .then( ret => {
    options = { 
      ...options, 
      socket: ret.socket.replace(/(\[)([a-zA-Z]+)(\])(\s)(\-){2}(\s)(\S+)(\s)(\-){2}(\s)(\S+)/, '$2')
    }
    let interfaceList = getInterfaceList(options.api)
    if (interfaceList.length === 0) {
      throw new Error('还没有API接口配置 !')
    }
    return inquirer.prompt([
      {
        type: 'list',
        name: 'interface',
        message: '请选择需要测试的接口：',
        choices: getInterfaceList(options.api)
      }
    ])
  })
  .then( ret => {
    options = { 
      ...options, 
      interface: ret.interface.replace(/(\[)([a-zA-Z\_]+)(\])(\s)(\-){2}(\s)(\S+)/, '$2')
    }
    let quests = getInterfaceParamQuests(options.api, options.interface)
    return inquirer.prompt(quests)
  })
  .then( ret => {
    options = { 
      ...options, 
      params: ret
    }
    runScript(options)
  })
  .catch( error => {
    console.log(`\n   ${error.message}\n`)
  })
}

const getInterfaceParams = (tag, name) => {
  let interfaces = getInterfaces(tag)
  let info = interfaces[name]
  return info.params || {}
}

const getInterfaceParamQuests = (tag, name) => {
  let params = getInterfaceParams(tag, name)
  let quests = []
  for (let item of _.keys(params)) {
    let info = params[item]
    quests.push({
      type: info.type,
      name: item,
      message: info.message,
      ...getValidData(info.pattern, info.required),
      ..._.has(info, 'format') ? { format: info.format.split(/\|/) } : null
    })
  }
  return quests
}

const getValidData = (pattern, required) => {
  if (!pattern && !required) return null
  return {
    validate: value => {
      if (required && _.isEmpty(value)) {
        return `此项不能为空`
      }
      if (pattern) {
        let reg = new RegExp(pattern)
        if (!reg.test(value)) {
          return '格式不对 ！'
        }
      }
      return true
    }
  }
}

const getInterfaceInfo = (tag, name) => {
  let interfaces = getInterfaces(tag)
  return interfaces[name]
}

const runScript = async (opts) => {
  console.log('')
  let spinner = ora(`    Socket Send ...`).start()
  let setting = GMC_CONF[opts.api]
  let pathname = setting.path
  let options = {
    ..._.pick(setting, ['base', 'head', 'gmPB', 'prefix']),
    path: path.resolve(GMC_PATH, pathname, 'pb'),
    socket: setting.proto
  }
  let rtsp = setting.socket[opts.socket].rtsp
  let socket = new Socket(rtsp)
  let protobuffer = new Protobuffer(options)
  let { createPBBuffer, makeData, decode, gameMessage } = protobuffer
  let info = getInterfaceInfo(opts.api, opts.interface)
  let Params = {
    ...opts.params,
    ..._.has(opts.params, 'begin') ? { begin: toDateTimestamp(opts.params.begin) } : null,
    ..._.has(opts.params, 'end') ? { end: toDateTimestamp(opts.params.end) } : null
  }
  let data = makeData(createPBBuffer(info.code, `game.${info.req}`, Params))
  let messageQuery = gameMessage(info.res)
  let result = null
  try {
    let buffer = await socket.send(data)
    let msgBase = decode(buffer, messageQuery)
    result = msgBase
  } catch (error) {
    result = error
  }
  setTimeout(() => {
    spinner.stop()
    console.log('✔   Complete Socket Send !\n')
    console.log(`  Interface: [${opts.interface}] -- ${info.msg}\n`)
    console.log(`  Request `)
    let t1 = Table(paramsTableHeader, object2array(Params), tableStyle)
    console.log(t1.render(), '\n')
    if (_.has(result, 'msghead')) {
      console.log(`  Response::Head `)
      let { msghead, msgbody } = result
      let t2 = Table(CMsgTableHeader, object2array(msghead), tableStyle)
      console.log(t2.render(), '\n')
      console.log(`  Response::Body `)
      let bodyer = []
      for (let item of msgbody.data) {
        bodyer.push(item.split(info.display.splitter))
      }
      let bodyHeader = getTableHeader(info.display.header)
      if (info.display.group) {
        let _bodyHeader = []
        for (let item of info.display.group) {
          _bodyHeader = _.concat(_bodyHeader, bodyHeader)
        }
        bodyHeader = _bodyHeader
        bodyer = getGroupList(bodyer, info.display.group)
      }
      let t3 = Table(bodyHeader, bodyer, tableStyle)
      console.log(t3.render(), '\n')
    }
    else {
      console.log(`  Response::Error \n`)
      throw result
    }
  }, 500)
}

const object2array = (obj) => {
  let arr = []
  for (let item of _.keys(obj)) {
    arr.push([item, obj[item]])
  }
  return arr
}

const toDateTimestamp = (dateString) => _.toInteger(moment(dateString).format('x'))

const getTableHeader = (header) => {
  let headerCell = []
  for (let item of header) {
    let info = _.zipObject(['value', 'align', 'width', 'paddingLeft', 'paddingRight'], item.split(/\|/))
    info = {
      ...info,
      ...info.align ? { align: info.align } : { align: 'center' },
      ...info.width ? { width: _.toInteger(info.width) } : { width: 20 },
      ...info.paddingLeft ? { paddingLeft: _.toInteger(info.paddingLeft) } : { paddingLeft: 0 },
      ...info.paddingRight ? { paddingRight: _.toInteger(info.paddingRight) } : { paddingRight: 0 }
    }
    headerCell.push(info)
  }
  return headerCell
}

const getGroupList = (list, group) => {
  group = toGroup(group)
  let groupList = []
  let groupArr = []
  let index = 0
  for (let item of group) {
    groupArr.push(list.slice(index, index + item))
    index += item
  }
  let maxLen = _.max(group)
  for (let i = 0; i < maxLen; i++) {
    let newItem = []
    for (let item of groupArr) {
      
      newItem.push(item.length < i + 1 ? ['--', '--'] : item[i])
    }
    groupList.push(_.concat(...newItem))
  }
  return groupList
}

const toGroup = (group) => {
  let arr = []
  for (let e of group) {
    arr.push(_.toInteger(e))
  }
  return arr
}